1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.android.launcher;
18
19 import android.app.WallpaperManager;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.ComponentName;
23 import android.content.res.TypedArray;
24 import android.graphics.Canvas;
25 import android.graphics.Rect;
26 import android.graphics.Region;
27 import android.graphics.drawable.Drawable;
28 import android.util.AttributeSet;
29 import android.view.MotionEvent;
30 import android.view.VelocityTracker;
31 import android.view.View;
32 import android.view.ViewConfiguration;
33 import android.view.ViewGroup;
34 import android.view.ViewParent;
35 import android.widget.Scroller;
36 import android.widget.TextView;
37 import android.os.Parcelable;
38 import android.os.Parcel;
39
40 import java.util.ArrayList;
41
42
43
44
45
46
47 public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller {
48 private static final int INVALID_SCREEN = -1;
49
50
51
52
53 private static final int SNAP_VELOCITY = 1000;
54
55 private int mDefaultScreen;
56
57 private final WallpaperManager mWallpaperManager;
58
59 private boolean mFirstLayout = true;
60
61 private int mCurrentScreen;
62 private int mNextScreen = INVALID_SCREEN;
63 private Scroller mScroller;
64 private VelocityTracker mVelocityTracker;
65
66
67
68
69 private CellLayout.CellInfo mDragInfo;
70
71
72
73
74 private int[] mTargetCell = null;
75
76 private float mLastMotionX;
77 private float mLastMotionY;
78
79 private final static int TOUCH_STATE_REST = 0;
80 private final static int TOUCH_STATE_SCROLLING = 1;
81
82 private int mTouchState = TOUCH_STATE_REST;
83
84 private OnLongClickListener mLongClickListener;
85
86 private Launcher mLauncher;
87 private DragController mDragger;
88
89
90
91
92 private CellLayout.CellInfo mVacantCache = null;
93
94 private int[] mTempCell = new int[2];
95 private int[] mTempEstimate = new int[2];
96
97 private boolean mAllowLongPress;
98 private boolean mLocked;
99
100 private int mTouchSlop;
101 private int mMaximumVelocity;
102
103 final Rect mDrawerBounds = new Rect();
104 final Rect mClipBounds = new Rect();
105 int mDrawerContentHeight;
106 int mDrawerContentWidth;
107
108
109
110
111
112
113
114 public Workspace(Context context, AttributeSet attrs) {
115 this(context, attrs, 0);
116 }
117
118
119
120
121
122
123
124
125 public Workspace(Context context, AttributeSet attrs, int defStyle) {
126 super(context, attrs, defStyle);
127
128 mWallpaperManager = WallpaperManager.getInstance(context);
129
130 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Workspace, defStyle, 0);
131 mDefaultScreen = a.getInt(R.styleable.Workspace_defaultScreen, 1);
132 a.recycle();
133
134 initWorkspace();
135 }
136
137
138
139
140 private void initWorkspace() {
141 mScroller = new Scroller(getContext());
142 mCurrentScreen = mDefaultScreen;
143 Launcher.setScreen(mCurrentScreen);
144
145 final ViewConfiguration configuration = ViewConfiguration.get(getContext());
146 mTouchSlop = configuration.getScaledTouchSlop();
147 mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
148 }
149
150 @Override
151 public void addView(View child, int index, LayoutParams params) {
152 if (!(child instanceof CellLayout)) {
153 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
154 }
155 super.addView(child, index, params);
156 }
157
158 @Override
159 public void addView(View child) {
160 if (!(child instanceof CellLayout)) {
161 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
162 }
163 super.addView(child);
164 }
165
166 @Override
167 public void addView(View child, int index) {
168 if (!(child instanceof CellLayout)) {
169 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
170 }
171 super.addView(child, index);
172 }
173
174 @Override
175 public void addView(View child, int width, int height) {
176 if (!(child instanceof CellLayout)) {
177 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
178 }
179 super.addView(child, width, height);
180 }
181
182 @Override
183 public void addView(View child, LayoutParams params) {
184 if (!(child instanceof CellLayout)) {
185 throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
186 }
187 super.addView(child, params);
188 }
189
190
191
192
193 Folder getOpenFolder() {
194 CellLayout currentScreen = (CellLayout) getChildAt(mCurrentScreen);
195 int count = currentScreen.getChildCount();
196 for (int i = 0; i < count; i++) {
197 View child = currentScreen.getChildAt(i);
198 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
199 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
200 return (Folder) child;
201 }
202 }
203 return null;
204 }
205
206 ArrayList<Folder> getOpenFolders() {
207 final int screens = getChildCount();
208 ArrayList<Folder> folders = new ArrayList<Folder>(screens);
209
210 for (int screen = 0; screen < screens; screen++) {
211 CellLayout currentScreen = (CellLayout) getChildAt(screen);
212 int count = currentScreen.getChildCount();
213 for (int i = 0; i < count; i++) {
214 View child = currentScreen.getChildAt(i);
215 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
216 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
217 folders.add((Folder) child);
218 break;
219 }
220 }
221 }
222
223 return folders;
224 }
225
226 boolean isDefaultScreenShowing() {
227 return mCurrentScreen == mDefaultScreen;
228 }
229
230
231
232
233
234
235 int getCurrentScreen() {
236 return mCurrentScreen;
237 }
238
239
240
241
242
243
244 void setCurrentScreen(int currentScreen) {
245 clearVacantCache();
246 mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1));
247 scrollTo(mCurrentScreen * getWidth(), 0);
248 invalidate();
249 }
250
251
252
253
254
255
256
257
258
259
260
261 void addInCurrentScreen(View child, int x, int y, int spanX, int spanY) {
262 addInScreen(child, mCurrentScreen, x, y, spanX, spanY, false);
263 }
264
265
266
267
268
269
270
271
272
273
274
275
276 void addInCurrentScreen(View child, int x, int y, int spanX, int spanY, boolean insert) {
277 addInScreen(child, mCurrentScreen, x, y, spanX, spanY, insert);
278 }
279
280
281
282
283
284
285
286
287
288
289
290
291 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY) {
292 addInScreen(child, screen, x, y, spanX, spanY, false);
293 }
294
295
296
297
298
299
300
301
302
303
304
305
306
307 void addInScreen(View child, int screen, int x, int y, int spanX, int spanY, boolean insert) {
308 if (screen < 0 || screen >= getChildCount()) {
309 throw new IllegalStateException("The screen must be >= 0 and < " + getChildCount());
310 }
311
312 clearVacantCache();
313
314 final CellLayout group = (CellLayout) getChildAt(screen);
315 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
316 if (lp == null) {
317 lp = new CellLayout.LayoutParams(x, y, spanX, spanY);
318 } else {
319 lp.cellX = x;
320 lp.cellY = y;
321 lp.cellHSpan = spanX;
322 lp.cellVSpan = spanY;
323 }
324 group.addView(child, insert ? 0 : -1, lp);
325 if (!(child instanceof Folder)) {
326 child.setOnLongClickListener(mLongClickListener);
327 }
328 }
329
330 void addWidget(View view, Widget widget, boolean insert) {
331 addInScreen(view, widget.screen, widget.cellX, widget.cellY, widget.spanX,
332 widget.spanY, insert);
333 }
334
335 CellLayout.CellInfo findAllVacantCells(boolean[] occupied) {
336 CellLayout group = (CellLayout) getChildAt(mCurrentScreen);
337 if (group != null) {
338 return group.findAllVacantCells(occupied, null);
339 }
340 return null;
341 }
342
343 CellLayout.CellInfo findAllVacantCellsFromModel() {
344 CellLayout group = (CellLayout) getChildAt(mCurrentScreen);
345 if (group != null) {
346 int countX = group.getCountX();
347 int countY = group.getCountY();
348 boolean occupied[][] = new boolean[countX][countY];
349 Launcher.getModel().findAllOccupiedCells(occupied, countX, countY, mCurrentScreen);
350 return group.findAllVacantCellsFromOccupied(occupied, countX, countY);
351 }
352 return null;
353 }
354
355 private void clearVacantCache() {
356 if (mVacantCache != null) {
357 mVacantCache.clearVacantCells();
358 mVacantCache = null;
359 }
360 }
361
362
363
364
365
366
367 @Override
368 public void setOnLongClickListener(OnLongClickListener l) {
369 mLongClickListener = l;
370 final int count = getChildCount();
371 for (int i = 0; i < count; i++) {
372 getChildAt(i).setOnLongClickListener(l);
373 }
374 }
375
376 private void updateWallpaperOffset() {
377 updateWallpaperOffset(getChildAt(getChildCount() - 1).getRight() - (mRight - mLeft));
378 }
379
380 private void updateWallpaperOffset(int scrollRange) {
381 mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 0 );
382 mWallpaperManager.setWallpaperOffsets(getWindowToken(), mScrollX / (float) scrollRange, 0);
383 }
384
385 @Override
386 public void computeScroll() {
387 if (mScroller.computeScrollOffset()) {
388 mScrollX = mScroller.getCurrX();
389 mScrollY = mScroller.getCurrY();
390 updateWallpaperOffset();
391 postInvalidate();
392 } else if (mNextScreen != INVALID_SCREEN) {
393 mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1));
394 Launcher.setScreen(mCurrentScreen);
395 mNextScreen = INVALID_SCREEN;
396 clearChildrenCache();
397 }
398 }
399
400 @Override
401 public boolean isOpaque() {
402 return false;
403 }
404
405 @Override
406 protected void dispatchDraw(Canvas canvas) {
407 boolean restore = false;
408
409
410
411
412 if (mLauncher.isDrawerUp()) {
413 final Rect clipBounds = mClipBounds;
414 canvas.getClipBounds(clipBounds);
415 clipBounds.offset(-mScrollX, -mScrollY);
416 if (mDrawerBounds.contains(clipBounds)) {
417 return;
418 }
419 } else if (mLauncher.isDrawerMoving()) {
420 restore = true;
421 canvas.save(Canvas.CLIP_SAVE_FLAG);
422
423 final View view = mLauncher.getDrawerHandle();
424 final int top = view.getTop() + view.getHeight();
425
426 canvas.clipRect(mScrollX, top, mScrollX + mDrawerContentWidth,
427 top + mDrawerContentHeight, Region.Op.DIFFERENCE);
428 }
429
430
431
432
433
434
435 boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN;
436
437 if (fastDraw) {
438 drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime());
439 } else {
440 final long drawingTime = getDrawingTime();
441
442 if (mNextScreen >= 0 && mNextScreen < getChildCount() &&
443 Math.abs(mCurrentScreen - mNextScreen) == 1) {
444 drawChild(canvas, getChildAt(mCurrentScreen), drawingTime);
445 drawChild(canvas, getChildAt(mNextScreen), drawingTime);
446 } else {
447
448 final int count = getChildCount();
449 for (int i = 0; i < count; i++) {
450 drawChild(canvas, getChildAt(i), drawingTime);
451 }
452 }
453 }
454
455 if (restore) {
456 canvas.restore();
457 }
458 }
459
460 @Override
461 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
462 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
463
464 final int width = MeasureSpec.getSize(widthMeasureSpec);
465 final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
466 if (widthMode != MeasureSpec.EXACTLY) {
467 throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
468 }
469
470 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
471 if (heightMode != MeasureSpec.EXACTLY) {
472 throw new IllegalStateException("Workspace can only be used in EXACTLY mode.");
473 }
474
475
476 final int count = getChildCount();
477 for (int i = 0; i < count; i++) {
478 getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
479 }
480
481 if (mFirstLayout) {
482 scrollTo(mCurrentScreen * width, 0);
483 updateWallpaperOffset(width * (getChildCount() - 1));
484 mFirstLayout = false;
485 }
486 }
487
488 @Override
489 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
490 int childLeft = 0;
491
492 final int count = getChildCount();
493 for (int i = 0; i < count; i++) {
494 final View child = getChildAt(i);
495 if (child.getVisibility() != View.GONE) {
496 final int childWidth = child.getMeasuredWidth();
497 child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight());
498 childLeft += childWidth;
499 }
500 }
501 }
502
503 @Override
504 public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
505 int screen = indexOfChild(child);
506 if (screen != mCurrentScreen || !mScroller.isFinished()) {
507 if (!mLauncher.isWorkspaceLocked()) {
508 snapToScreen(screen);
509 }
510 return true;
511 }
512 return false;
513 }
514
515 @Override
516 protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
517 if (mLauncher.isDrawerDown()) {
518 final Folder openFolder = getOpenFolder();
519 if (openFolder != null) {
520 return openFolder.requestFocus(direction, previouslyFocusedRect);
521 } else {
522 int focusableScreen;
523 if (mNextScreen != INVALID_SCREEN) {
524 focusableScreen = mNextScreen;
525 } else {
526 focusableScreen = mCurrentScreen;
527 }
528 getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect);
529 }
530 }
531 return false;
532 }
533
534 @Override
535 public boolean dispatchUnhandledMove(View focused, int direction) {
536 if (direction == View.FOCUS_LEFT) {
537 if (getCurrentScreen() > 0) {
538 snapToScreen(getCurrentScreen() - 1);
539 return true;
540 }
541 } else if (direction == View.FOCUS_RIGHT) {
542 if (getCurrentScreen() < getChildCount() - 1) {
543 snapToScreen(getCurrentScreen() + 1);
544 return true;
545 }
546 }
547 return super.dispatchUnhandledMove(focused, direction);
548 }
549
550 @Override
551 public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
552 if (mLauncher.isDrawerDown()) {
553 final Folder openFolder = getOpenFolder();
554 if (openFolder == null) {
555 getChildAt(mCurrentScreen).addFocusables(views, direction);
556 if (direction == View.FOCUS_LEFT) {
557 if (mCurrentScreen > 0) {
558 getChildAt(mCurrentScreen - 1).addFocusables(views, direction);
559 }
560 } else if (direction == View.FOCUS_RIGHT){
561 if (mCurrentScreen < getChildCount() - 1) {
562 getChildAt(mCurrentScreen + 1).addFocusables(views, direction);
563 }
564 }
565 } else {
566 openFolder.addFocusables(views, direction);
567 }
568 }
569 }
570
571 @Override
572 public boolean onInterceptTouchEvent(MotionEvent ev) {
573 if (mLocked || !mLauncher.isDrawerDown()) {
574 return true;
575 }
576
577
578
579
580
581
582
583
584
585
586
587
588 final int action = ev.getAction();
589 if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
590 return true;
591 }
592
593 final float x = ev.getX();
594 final float y = ev.getY();
595
596 switch (action) {
597 case MotionEvent.ACTION_MOVE:
598
599
600
601
602
603
604
605
606
607 final int xDiff = (int) Math.abs(x - mLastMotionX);
608 final int yDiff = (int) Math.abs(y - mLastMotionY);
609
610 final int touchSlop = mTouchSlop;
611 boolean xMoved = xDiff > touchSlop;
612 boolean yMoved = yDiff > touchSlop;
613
614 if (xMoved || yMoved) {
615
616 if (xMoved) {
617
618 mTouchState = TOUCH_STATE_SCROLLING;
619 enableChildrenCache();
620 }
621
622 if (mAllowLongPress) {
623 mAllowLongPress = false;
624
625
626
627 final View currentScreen = getChildAt(mCurrentScreen);
628 currentScreen.cancelLongPress();
629 }
630 }
631 break;
632
633 case MotionEvent.ACTION_DOWN:
634
635 mLastMotionX = x;
636 mLastMotionY = y;
637 mAllowLongPress = true;
638
639
640
641
642
643
644 mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING;
645 break;
646
647 case MotionEvent.ACTION_CANCEL:
648 case MotionEvent.ACTION_UP:
649
650 clearChildrenCache();
651 mTouchState = TOUCH_STATE_REST;
652 mAllowLongPress = false;
653 break;
654 }
655
656
657
658
659
660 return mTouchState != TOUCH_STATE_REST;
661 }
662
663 void enableChildrenCache() {
664 final int count = getChildCount();
665 for (int i = 0; i < count; i++) {
666 final CellLayout layout = (CellLayout) getChildAt(i);
667 layout.setChildrenDrawnWithCacheEnabled(true);
668 layout.setChildrenDrawingCacheEnabled(true);
669 }
670 }
671
672 void clearChildrenCache() {
673 final int count = getChildCount();
674 for (int i = 0; i < count; i++) {
675 final CellLayout layout = (CellLayout) getChildAt(i);
676 layout.setChildrenDrawnWithCacheEnabled(false);
677 }
678 }
679
680 @Override
681 public boolean onTouchEvent(MotionEvent ev) {
682 if (mLocked || !mLauncher.isDrawerDown()) {
683 return true;
684 }
685
686 if (mVelocityTracker == null) {
687 mVelocityTracker = VelocityTracker.obtain();
688 }
689 mVelocityTracker.addMovement(ev);
690
691 final int action = ev.getAction();
692 final float x = ev.getX();
693
694 switch (action) {
695 case MotionEvent.ACTION_DOWN:
696
697
698
699
700 if (!mScroller.isFinished()) {
701 mScroller.abortAnimation();
702 }
703
704
705 mLastMotionX = x;
706 break;
707 case MotionEvent.ACTION_MOVE:
708 if (mTouchState == TOUCH_STATE_SCROLLING) {
709
710 final int deltaX = (int) (mLastMotionX - x);
711 mLastMotionX = x;
712
713 if (deltaX < 0) {
714 if (mScrollX > 0) {
715 scrollBy(Math.max(-mScrollX, deltaX), 0);
716 updateWallpaperOffset();
717 }
718 } else if (deltaX > 0) {
719 final int availableToScroll = getChildAt(getChildCount() - 1).getRight() -
720 mScrollX - getWidth();
721 if (availableToScroll > 0) {
722 scrollBy(Math.min(availableToScroll, deltaX), 0);
723 updateWallpaperOffset();
724 }
725 }
726 }
727 break;
728 case MotionEvent.ACTION_UP:
729 if (mTouchState == TOUCH_STATE_SCROLLING) {
730 final VelocityTracker velocityTracker = mVelocityTracker;
731 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
732 int velocityX = (int) velocityTracker.getXVelocity();
733
734 if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
735
736 snapToScreen(mCurrentScreen - 1);
737 } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) {
738
739 snapToScreen(mCurrentScreen + 1);
740 } else {
741 snapToDestination();
742 }
743
744 if (mVelocityTracker != null) {
745 mVelocityTracker.recycle();
746 mVelocityTracker = null;
747 }
748 }
749 mTouchState = TOUCH_STATE_REST;
750 break;
751 case MotionEvent.ACTION_CANCEL:
752 mTouchState = TOUCH_STATE_REST;
753 }
754
755 return true;
756 }
757
758 private void snapToDestination() {
759 final int screenWidth = getWidth();
760 final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth;
761
762 snapToScreen(whichScreen);
763 }
764
765 void snapToScreen(int whichScreen) {
766 if (!mScroller.isFinished()) return;
767
768 clearVacantCache();
769 enableChildrenCache();
770
771 whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
772 boolean changingScreens = whichScreen != mCurrentScreen;
773
774 mNextScreen = whichScreen;
775
776 View focusedChild = getFocusedChild();
777 if (focusedChild != null && changingScreens && focusedChild == getChildAt(mCurrentScreen)) {
778 focusedChild.clearFocus();
779 }
780
781 final int newX = whichScreen * getWidth();
782 final int delta = newX - mScrollX;
783 mScroller.startScroll(mScrollX, 0, delta, 0, Math.abs(delta) * 2);
784 invalidate();
785 }
786
787 void startDrag(CellLayout.CellInfo cellInfo) {
788 View child = cellInfo.cell;
789
790
791
792 if (!child.isInTouchMode() && !(child instanceof Search)) {
793 return;
794 }
795
796 mDragInfo = cellInfo;
797 mDragInfo.screen = mCurrentScreen;
798
799 CellLayout current = ((CellLayout) getChildAt(mCurrentScreen));
800
801 current.onDragChild(child);
802 mDragger.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
803 invalidate();
804 }
805
806 @Override
807 protected Parcelable onSaveInstanceState() {
808 final SavedState state = new SavedState(super.onSaveInstanceState());
809 state.currentScreen = mCurrentScreen;
810 return state;
811 }
812
813 @Override
814 protected void onRestoreInstanceState(Parcelable state) {
815 SavedState savedState = (SavedState) state;
816 super.onRestoreInstanceState(savedState.getSuperState());
817 if (savedState.currentScreen != -1) {
818 mCurrentScreen = savedState.currentScreen;
819 Launcher.setScreen(mCurrentScreen);
820 }
821 }
822
823 void addApplicationShortcut(ApplicationInfo info, CellLayout.CellInfo cellInfo,
824 boolean insertAtFirst) {
825 final CellLayout layout = (CellLayout) getChildAt(cellInfo.screen);
826 final int[] result = new int[2];
827
828 layout.cellToPoint(cellInfo.cellX, cellInfo.cellY, result);
829 onDropExternal(result[0], result[1], info, layout, insertAtFirst);
830 }
831
832 public void onDrop(DragSource source, int x, int y, int xOffset, int yOffset, Object dragInfo) {
833 final CellLayout cellLayout = getCurrentDropLayout();
834 if (source != this) {
835 onDropExternal(x - xOffset, y - yOffset, dragInfo, cellLayout);
836 } else {
837
838 if (mDragInfo != null) {
839 final View cell = mDragInfo.cell;
840 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
841 if (index != mDragInfo.screen) {
842 final CellLayout originalCellLayout = (CellLayout) getChildAt(mDragInfo.screen);
843 originalCellLayout.removeView(cell);
844 cellLayout.addView(cell);
845 }
846 mTargetCell = estimateDropCell(x - xOffset, y - yOffset,
847 mDragInfo.spanX, mDragInfo.spanY, cell, cellLayout, mTargetCell);
848 cellLayout.onDropChild(cell, mTargetCell);
849
850 final ItemInfo info = (ItemInfo)cell.getTag();
851 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) cell.getLayoutParams();
852 LauncherModel.moveItemInDatabase(mLauncher, info,
853 LauncherSettings.Favorites.CONTAINER_DESKTOP, index, lp.cellX, lp.cellY);
854 }
855 }
856 }
857
858 public void onDragEnter(DragSource source, int x, int y, int xOffset, int yOffset,
859 Object dragInfo) {
860 clearVacantCache();
861 }
862
863 public void onDragOver(DragSource source, int x, int y, int xOffset, int yOffset,
864 Object dragInfo) {
865 }
866
867 public void onDragExit(DragSource source, int x, int y, int xOffset, int yOffset,
868 Object dragInfo) {
869 clearVacantCache();
870 }
871
872 private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout) {
873 onDropExternal(x, y, dragInfo, cellLayout, false);
874 }
875
876 private void onDropExternal(int x, int y, Object dragInfo, CellLayout cellLayout,
877 boolean insertAtFirst) {
878
879 ItemInfo info = (ItemInfo) dragInfo;
880
881 View view;
882
883 switch (info.itemType) {
884 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
885 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
886 if (info.container == NO_ID) {
887
888 info = new ApplicationInfo((ApplicationInfo) info);
889 }
890 view = mLauncher.createShortcut(R.layout.application, cellLayout,
891 (ApplicationInfo) info);
892 break;
893 case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
894 view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
895 (ViewGroup) getChildAt(mCurrentScreen), ((UserFolderInfo) info));
896 break;
897 default:
898 throw new IllegalStateException("Unknown item type: " + info.itemType);
899 }
900
901 cellLayout.addView(view, insertAtFirst ? 0 : -1);
902 view.setOnLongClickListener(mLongClickListener);
903 mTargetCell = estimateDropCell(x, y, 1, 1, view, cellLayout, mTargetCell);
904 cellLayout.onDropChild(view, mTargetCell);
905 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
906
907 final LauncherModel model = Launcher.getModel();
908 model.addDesktopItem(info);
909 LauncherModel.addOrMoveItemInDatabase(mLauncher, info,
910 LauncherSettings.Favorites.CONTAINER_DESKTOP, mCurrentScreen, lp.cellX, lp.cellY);
911 }
912
913
914
915
916
917 private CellLayout getCurrentDropLayout() {
918 int index = mScroller.isFinished() ? mCurrentScreen : mNextScreen;
919 return (CellLayout) getChildAt(index);
920 }
921
922
923
924
925 public boolean acceptDrop(DragSource source, int x, int y,
926 int xOffset, int yOffset, Object dragInfo) {
927 final CellLayout layout = getCurrentDropLayout();
928 final CellLayout.CellInfo cellInfo = mDragInfo;
929 final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
930 final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
931
932 if (mVacantCache == null) {
933 final View ignoreView = cellInfo == null ? null : cellInfo.cell;
934 mVacantCache = layout.findAllVacantCells(null, ignoreView);
935 }
936
937 return mVacantCache.findCellForSpan(mTempEstimate, spanX, spanY, false);
938 }
939
940
941
942
943 public Rect estimateDropLocation(int x, int y, int xOffset, int yOffset, Rect recycle) {
944 final CellLayout layout = getCurrentDropLayout();
945
946 final CellLayout.CellInfo cellInfo = mDragInfo;
947 final int spanX = cellInfo == null ? 1 : cellInfo.spanX;
948 final int spanY = cellInfo == null ? 1 : cellInfo.spanY;
949 final View ignoreView = cellInfo == null ? null : cellInfo.cell;
950
951 final Rect location = recycle != null ? recycle : new Rect();
952
953
954 int[] dropCell = estimateDropCell(x - xOffset, y - yOffset,
955 spanX, spanY, ignoreView, layout, mTempCell);
956
957 if (dropCell == null) {
958 return null;
959 }
960
961 layout.cellToPoint(dropCell[0], dropCell[1], mTempEstimate);
962 location.left = mTempEstimate[0];
963 location.top = mTempEstimate[1];
964
965 layout.cellToPoint(dropCell[0] + spanX, dropCell[1] + spanY, mTempEstimate);
966 location.right = mTempEstimate[0];
967 location.bottom = mTempEstimate[1];
968
969 return location;
970 }
971
972
973
974
975 private int[] estimateDropCell(int pixelX, int pixelY,
976 int spanX, int spanY, View ignoreView, CellLayout layout, int[] recycle) {
977
978 if (mVacantCache == null) {
979 mVacantCache = layout.findAllVacantCells(null, ignoreView);
980 }
981
982
983 return layout.findNearestVacantArea(pixelX, pixelY, spanX, spanY, mVacantCache, recycle);
984 }
985
986 void setLauncher(Launcher launcher) {
987 mLauncher = launcher;
988 }
989
990 public void setDragger(DragController dragger) {
991 mDragger = dragger;
992 }
993
994 public void onDropCompleted(View target, boolean success) {
995
996 clearVacantCache();
997
998 if (success){
999 if (target != this && mDragInfo != null) {
1000 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1001 cellLayout.removeView(mDragInfo.cell);
1002 final Object tag = mDragInfo.cell.getTag();
1003 Launcher.getModel().removeDesktopItem((ItemInfo) tag);
1004 }
1005 } else {
1006 if (mDragInfo != null) {
1007 final CellLayout cellLayout = (CellLayout) getChildAt(mDragInfo.screen);
1008 cellLayout.onDropAborted(mDragInfo.cell);
1009 }
1010 }
1011
1012 mDragInfo = null;
1013 }
1014
1015 public void scrollLeft() {
1016 clearVacantCache();
1017 if (mNextScreen == INVALID_SCREEN && mCurrentScreen > 0 && mScroller.isFinished()) {
1018 snapToScreen(mCurrentScreen - 1);
1019 }
1020 }
1021
1022 public void scrollRight() {
1023 clearVacantCache();
1024 if (mNextScreen == INVALID_SCREEN && mCurrentScreen < getChildCount() -1 &&
1025 mScroller.isFinished()) {
1026 snapToScreen(mCurrentScreen + 1);
1027 }
1028 }
1029
1030 public int getScreenForView(View v) {
1031 int result = -1;
1032 if (v != null) {
1033 ViewParent vp = v.getParent();
1034 int count = getChildCount();
1035 for (int i = 0; i < count; i++) {
1036 if (vp == getChildAt(i)) {
1037 return i;
1038 }
1039 }
1040 }
1041 return result;
1042 }
1043
1044
1045
1046
1047 private Search findSearchWidget(CellLayout screen) {
1048 final int count = screen.getChildCount();
1049 for (int i = 0; i < count; i++) {
1050 View v = screen.getChildAt(i);
1051 if (v instanceof Search) {
1052 return (Search) v;
1053 }
1054 }
1055 return null;
1056 }
1057
1058
1059
1060
1061
1062 public Search findSearchWidgetOnCurrentScreen() {
1063 CellLayout currentScreen = (CellLayout)getChildAt(mCurrentScreen);
1064 return findSearchWidget(currentScreen);
1065 }
1066
1067 public Folder getFolderForTag(Object tag) {
1068 int screenCount = getChildCount();
1069 for (int screen = 0; screen < screenCount; screen++) {
1070 CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1071 int count = currentScreen.getChildCount();
1072 for (int i = 0; i < count; i++) {
1073 View child = currentScreen.getChildAt(i);
1074 CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
1075 if (lp.cellHSpan == 4 && lp.cellVSpan == 4 && child instanceof Folder) {
1076 Folder f = (Folder) child;
1077 if (f.getInfo() == tag) {
1078 return f;
1079 }
1080 }
1081 }
1082 }
1083 return null;
1084 }
1085
1086 public View getViewForTag(Object tag) {
1087 int screenCount = getChildCount();
1088 for (int screen = 0; screen < screenCount; screen++) {
1089 CellLayout currentScreen = ((CellLayout) getChildAt(screen));
1090 int count = currentScreen.getChildCount();
1091 for (int i = 0; i < count; i++) {
1092 View child = currentScreen.getChildAt(i);
1093 if (child.getTag() == tag) {
1094 return child;
1095 }
1096 }
1097 }
1098 return null;
1099 }
1100
1101
1102
1103
1104
1105
1106 public void unlock() {
1107 mLocked = false;
1108 }
1109
1110
1111
1112
1113
1114
1115 public void lock() {
1116 mLocked = true;
1117 }
1118
1119
1120
1121
1122 public boolean allowLongPress() {
1123 return mAllowLongPress;
1124 }
1125
1126
1127
1128
1129
1130 public void setAllowLongPress(boolean allowLongPress) {
1131 mAllowLongPress = allowLongPress;
1132 }
1133
1134 void removeShortcutsForPackage(String packageName) {
1135 final ArrayList<View> childrenToRemove = new ArrayList<View>();
1136 final LauncherModel model = Launcher.getModel();
1137 final int count = getChildCount();
1138
1139 for (int i = 0; i < count; i++) {
1140 final CellLayout layout = (CellLayout) getChildAt(i);
1141 int childCount = layout.getChildCount();
1142
1143 childrenToRemove.clear();
1144
1145 for (int j = 0; j < childCount; j++) {
1146 final View view = layout.getChildAt(j);
1147 Object tag = view.getTag();
1148
1149 if (tag instanceof ApplicationInfo) {
1150 final ApplicationInfo info = (ApplicationInfo) tag;
1151
1152
1153
1154 final Intent intent = info.intent;
1155 final ComponentName name = intent.getComponent();
1156
1157 if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
1158 name != null && packageName.equals(name.getPackageName())) {
1159 model.removeDesktopItem(info);
1160 LauncherModel.deleteItemFromDatabase(mLauncher, info);
1161 childrenToRemove.add(view);
1162 }
1163 } else if (tag instanceof UserFolderInfo) {
1164 final UserFolderInfo info = (UserFolderInfo) tag;
1165 final ArrayList<ApplicationInfo> contents = info.contents;
1166 final ArrayList<ApplicationInfo> toRemove = new ArrayList<ApplicationInfo>(1);
1167 final int contentsCount = contents.size();
1168 boolean removedFromFolder = false;
1169
1170 for (int k = 0; k < contentsCount; k++) {
1171 final ApplicationInfo appInfo = contents.get(k);
1172 final Intent intent = appInfo.intent;
1173 final ComponentName name = intent.getComponent();
1174
1175 if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
1176 name != null && packageName.equals(name.getPackageName())) {
1177 toRemove.add(appInfo);
1178 LauncherModel.deleteItemFromDatabase(mLauncher, appInfo);
1179 removedFromFolder = true;
1180 }
1181 }
1182
1183 contents.removeAll(toRemove);
1184 if (removedFromFolder) {
1185 final Folder folder = getOpenFolder();
1186 if (folder != null) folder.notifyDataSetChanged();
1187 }
1188 }
1189 }
1190
1191 childCount = childrenToRemove.size();
1192 for (int j = 0; j < childCount; j++) {
1193 layout.removeViewInLayout(childrenToRemove.get(j));
1194 }
1195
1196 if (childCount > 0) {
1197 layout.requestLayout();
1198 layout.invalidate();
1199 }
1200 }
1201 }
1202
1203 void updateShortcutsForPackage(String packageName) {
1204 final int count = getChildCount();
1205 for (int i = 0; i < count; i++) {
1206 final CellLayout layout = (CellLayout) getChildAt(i);
1207 int childCount = layout.getChildCount();
1208 for (int j = 0; j < childCount; j++) {
1209 final View view = layout.getChildAt(j);
1210 Object tag = view.getTag();
1211 if (tag instanceof ApplicationInfo) {
1212 ApplicationInfo info = (ApplicationInfo) tag;
1213
1214
1215
1216 final Intent intent = info.intent;
1217 final ComponentName name = intent.getComponent();
1218 if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
1219 Intent.ACTION_MAIN.equals(intent.getAction()) && name != null &&
1220 packageName.equals(name.getPackageName())) {
1221
1222 final Drawable icon = Launcher.getModel().getApplicationInfoIcon(
1223 mLauncher.getPackageManager(), info);
1224 if (icon != null && icon != info.icon) {
1225 info.icon.setCallback(null);
1226 info.icon = Utilities.createIconThumbnail(icon, mContext);
1227 info.filtered = true;
1228 ((TextView) view).setCompoundDrawablesWithIntrinsicBounds(null,
1229 info.icon, null, null);
1230 }
1231 }
1232 }
1233 }
1234 }
1235 }
1236
1237 void moveToDefaultScreen() {
1238 snapToScreen(mDefaultScreen);
1239 getChildAt(mDefaultScreen).requestFocus();
1240 }
1241
1242 public static class SavedState extends BaseSavedState {
1243 int currentScreen = -1;
1244
1245 SavedState(Parcelable superState) {
1246 super(superState);
1247 }
1248
1249 private SavedState(Parcel in) {
1250 super(in);
1251 currentScreen = in.readInt();
1252 }
1253
1254 @Override
1255 public void writeToParcel(Parcel out, int flags) {
1256 super.writeToParcel(out, flags);
1257 out.writeInt(currentScreen);
1258 }
1259
1260 public static final Parcelable.Creator<SavedState> CREATOR =
1261 new Parcelable.Creator<SavedState>() {
1262 public SavedState createFromParcel(Parcel in) {
1263 return new SavedState(in);
1264 }
1265
1266 public SavedState[] newArray(int size) {
1267 return new SavedState[size];
1268 }
1269 };
1270 }
1271 }